package calc

import (
	"errors"
	"fmt"
	"log/slog"
	"net/http"

	"github.com/go-chi/chi/v5/middleware"
	"github.com/go-chi/render"
	"github.com/shopspring/decimal"
	"gitverse.ru/z3nyk3y/investcalc/internal/helpers/logger/sl"
	"gitverse.ru/z3nyk3y/investcalc/internal/http-server/response"
	"gitverse.ru/z3nyk3y/investcalc/internal/models"
	"gitverse.ru/z3nyk3y/investcalc/internal/usecases/calculation"
)

var ErrInvalidTask = errors.New("invalid task")
var ErrTargetValue = errors.New("target must be from 1 to 1 000 000 000 000 000")
var ErrStartUpCapValue = errors.New("startup capital must be from 1 to 1 000 000 000 000 000")
var ErrInvestPeriodStruct = errors.New("invest period cannot be null")
var ErrInvestPeriodAmount = errors.New("invest period must be from 1 to 1 000 000 000 000 000")
var ErrInvestPeriod = errors.New("invalid investment period")
var ErrCoefficientValue = errors.New("coefficient must be from 1 to 100")
var ErrReinvestPeriod = errors.New("invalid reinvest period")

type Handler struct {
	log *slog.Logger
}

func NewHandler(log *slog.Logger) *Handler {
	return &Handler{
		log: log,
	}
}

func (h *Handler) validate(calc *models.Calculator) error {

	if calc.ToCalculate != models.TARGET && calc.ToCalculate != models.START_UP_CAPITAL &&
		calc.ToCalculate != models.INVEST_PERIOD && calc.ToCalculate != models.COEFFICIENT && calc.ToCalculate != models.ADDICTIONAL_INVESTMENT {
		return ErrInvalidTask
	}
	fmt.Println(calc.StartUpCapital.Sign())
	fmt.Println(calc.StartUpCapital.String())
	if (calc.Target.Sign() != 1 || calc.Target.GreaterThan(decimal.New(1_000_000_000_000_000, 0))) && calc.ToCalculate != models.TARGET {
		return ErrTargetValue
	}
	if calc.StartUpCapital.Sign() != 1 || calc.StartUpCapital.GreaterThan(decimal.New(1_000_000_000_000_000, 0)) && calc.ToCalculate != models.START_UP_CAPITAL {
		return ErrStartUpCapValue
	}
	if calc.InvestPeriod == nil && calc.ToCalculate != models.INVEST_PERIOD {
		return ErrInvestPeriodStruct
	} else {
		if calc.InvestPeriod.Amount <= 0 {
			return ErrInvestPeriodAmount
		}
		if calc.InvestPeriod.Period != models.YEARS && calc.InvestPeriod.Period != models.MONTHS {
			return ErrInvestPeriod
		}
	}
	if calc.Coefficient.Sign() != 1 || calc.Coefficient.GreaterThan(decimal.New(100, 0)) && calc.ToCalculate != models.COEFFICIENT {
		return ErrCoefficientValue

	}
	if calc.ReinvestPeriod != models.MONTHLY && calc.ReinvestPeriod != models.QUARTERLY && calc.ReinvestPeriod != models.TWICE_A_YEAR && calc.ReinvestPeriod != models.YEARLY && calc.ReinvestPeriod != models.NEVER {
		return ErrReinvestPeriod
	}

	return nil
}

func (h *Handler) Calc() http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		const op = "handlers.url.calc.New"

		log := h.log.With(
			slog.String("op", op),
			slog.String("request_id", middleware.GetReqID(r.Context())),
		)

		var calc = &models.Calculator{}

		err := render.DecodeJSON(r.Body, calc)
		if err != nil {
			log.Error("failed to decode request", sl.Err(err))
			w.Header().Add("Access-Control-Allow-Origin", "*")
			w.Header().Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
			w.Header().Add("Access-Control-Allow-Headers", "Content-Type")
			render.Status(r, http.StatusBadRequest)
			render.JSON(w, r, response.Error("failed to decode request"))
			return
		}

		log.Info("request received and decoded")

		err = h.validate(calc)

		if err != nil {
			log.Error("failed to validate request", sl.Err(err))
			w.Header().Add("Access-Control-Allow-Origin", "*")
			w.Header().Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
			w.Header().Add("Access-Control-Allow-Headers", "Content-Type")
			render.Status(r, http.StatusBadRequest)
			render.JSON(w, r, response.Error(err.Error()))
			return

		}

		uc := calculation.NewUseCase(calc)

		ans, err := uc.Calc()
		ans = ans.RoundBank(2)
		if err != nil {
			log.Error("failed to calculate", sl.Err(err))
			w.Header().Add("Access-Control-Allow-Origin", "*")
			w.Header().Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
			w.Header().Add("Access-Control-Allow-Headers", "Content-Type")
			render.Status(r, http.StatusBadRequest)

			render.JSON(w, r, response.Error(err.Error()))
			return

		} else {
			log.Info("calculation completed")
			w.Header().Add("Access-Control-Allow-Origin", "*")
			w.Header().Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
			w.Header().Add("Access-Control-Allow-Headers", "Content-Type")
			render.JSON(w, r, response.Success(ans.String()))
			return
		}

	}

	return http.HandlerFunc(fn)
}
